package it.valeandre.omino.image;


import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;

import android.content.Context;
import android.os.Environment;
import android.util.Log;

public final class ImageDownloadService {
	public static final String RSA_PATH = "/RSA/";
	public static final long ONE_MINUTE = 60 * 1000;
	public static final long TEN_MINUTES = 10 * ONE_MINUTE;
	public static final long ONE_HOUR = 60 * ONE_MINUTE;
	public static final long ONE_DAY = 24 * ONE_HOUR;
	public static final long ONE_WEEK = 7 * ONE_DAY;
	public static final long ONE_MONTH = 31 * ONE_DAY;
	private static ImageDownloadService _instance;
	private boolean mIsShutdown;
	private File mCacheDirectory;
	private ExecutorService mStandardExecutor;
	private ExecutorService mDiscardableExecutor;

	private abstract class NetworkTask implements Runnable {
		protected ImageDownloadServiceListener mListener;

		protected NetworkTask(ImageDownloadServiceListener listener) {
			mListener = listener;
		}

		protected void saveToFile(InputStream input, File file) throws Exception {
			if (!file.exists()) {
				file.getParentFile().mkdirs();
				file.createNewFile();
			}

			FileOutputStream output = new FileOutputStream(file);

			byte[] buffer = new byte[16384];
			int read;

			while ((read = input.read(buffer)) != -1) {
				// //System.out.println("Writing " + read + " bytes");
				output.write(buffer, 0, read);
			}

			output.flush();
			output.close();
		}
	}
	
//	public boolean isImageAlreadyDownloaded(String url) {
//		String filename = byteArrayToHexString(md5(url.getBytes()));
//		File file = new File(mCacheDirectory, filename);
//		return file.exists();
//	}

	private class DownloadImageTask extends NetworkTask {
		private final String mUrl;

		DownloadImageTask(String url, ImageDownloadServiceListener listener) {
			super(listener);
			mUrl = url;
		}

		public void run() {

			try {
				String filename = Environment.getExternalStorageDirectory() + RSA_PATH + byteArrayToHexString(md5(mUrl.getBytes()));
				String tmpfilename = filename + ".tmp";

				/*
				 * Cancelliamo l'eventuale file temporaneo precedente...
				 */
				File tmpfile = new File(mCacheDirectory, tmpfilename);
				if (tmpfile.exists()) {
					try {
						tmpfile.delete();
					} catch (Exception e) {
					}
				}

				File file = new File(mCacheDirectory, filename);

				if (file.exists()) {
					try {
						if (mListener != null && ! mIsShutdown) {
							mListener.downloadImagePerformed(mUrl, file.getAbsolutePath());
						}
						return;
					} catch (Exception e) {
						try {
							file.delete();
						} catch (Exception e2) {
						}

					}
				}

				DefaultHttpClient client = new DefaultHttpClient();

				HttpGet request = new HttpGet(mUrl);

				HttpResponse response = client.execute(request);

				if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {

					saveToFile(response.getEntity().getContent(), tmpfile);

					// se tutto � andato bene...

					tmpfile.renameTo(file);

					if (mListener != null && ! mIsShutdown) {
						mListener.downloadImagePerformed(mUrl, file.getAbsolutePath());
					}
				} else {
					throw new Exception("Status code: " + response.getStatusLine().getStatusCode());
				}

			} catch (Exception e) {
				/*
				 * Stampiamo l'eccezione, ma in realta' non c'e' niente da fare:
				 * l'immagine non sara' disponibile e si usera' un placeholder.
				 */
				mListener.downloadImageFailed(mUrl, e);
			}
		}
	}
	
	public static ImageDownloadService getInstance() {
		if (_instance == null) {
			_instance = new ImageDownloadService();
		}

		return _instance;
	}

	private ImageDownloadService() {
		mStandardExecutor = Executors.newFixedThreadPool(2);
		mDiscardableExecutor = Executors.newFixedThreadPool(2);
	}

	public void downloadImage(Context context, String url, ImageDownloadServiceListener listener) {
		downloadImage(url, false, listener);
	}

	public void downloadImage(String url, boolean discardable, ImageDownloadServiceListener listener) {
		DownloadImageTask task = new DownloadImageTask(url, listener);

		if (discardable) {
			mDiscardableExecutor.execute(task);
		} else {
			mStandardExecutor.execute(task);
		}
	}

	public void setCacheDirectory(File cacheDirectory) {
		mCacheDirectory = cacheDirectory;
	}

	public void shutdown() {
		_instance = null;
		mIsShutdown = true;

		mStandardExecutor.shutdown();
		mDiscardableExecutor.shutdown();
	}

	public static final String byteArrayToHexString(byte[] data) {
		StringBuffer buffer = new StringBuffer(data.length * 2);

		for (int i = 0; i < data.length; i++) {
			int v = data[i] & 0xff;
			if (v < 16) {
				buffer.append('0');
			}
			buffer.append(Integer.toHexString(v));
		}
		return buffer.toString().toUpperCase();
	}

	public static final byte[] hexStringToByteArray(String hexString) {
		byte[] data = new byte[hexString.length() / 2];

		for (int i = 0; i < data.length; i++) {
			data[i] = (byte) (0xFF & Integer.parseInt(hexString.substring(i * 2, i * 2 + 2), 16));
		}

		return data;
	}

	public static final byte[] md5(byte[] data) {
		try {
			MessageDigest digest = MessageDigest.getInstance("MD5");
			digest.update(data);
			return digest.digest();
		} catch (NoSuchAlgorithmException e) {
			return null;
		}
	}

	public static final void clearCacheDirectory(final long expiration, final File cacheDirectory) {
		Thread t = new Thread() {
			@Override
			public void run() {
				////System.out.println("Clearning cache");

				try {
					long now = System.currentTimeMillis();

					File[] files = cacheDirectory.listFiles();
					for (File file : files) {
						if (file.isFile()) {
							if (now - file.lastModified() > expiration) {
								////System.out.println("Deleting file: " + file.getAbsolutePath());
								try {
									file.delete();
								} catch (Exception e) {
								}
							} else {
								////System.out.println("File NOT deleted: " + file.getAbsolutePath());
							}
						}
					}
				} catch (Exception e) {
				}
			}
		};
		t.start();
	}
}
